package com.gitee.beiding.spring.config;

import com.gitee.beiding.spring.annotation.Handler;
import com.gitee.beiding.timer.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/*
    自动扫描所有处理器
 */
@Configuration
class AutoConfig {


    //不能在创建Bean的时候获取Bean
    @Bean
    MyTimer defaultMyTimer(ApplicationContext applicationContext, @Autowired(required = false) List<TaskHandler> list, @Autowired(required = false) TaskRepository taskRepository) {

        List<TaskHandler> taskHandlers = new ArrayList<>();

        if (list != null) {
            taskHandlers.addAll(list);
        }

        String[] names = applicationContext.getBeanDefinitionNames();
        for (String name : names) {

            if (name.equals("defaultMyTimer")) {
                continue;
            }

            System.out.println(name);

            Object bean = applicationContext.getBean(name);
            if (bean != null) {
                Class<?> beanClass = bean.getClass();

                //获取声明的方法
                Method[] declaredMethods = beanClass.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    Handler handler = declaredMethod.getAnnotation(Handler.class);
                    if (handler != null) {
                        Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
                        String n = handler.name().trim();
                        n = n.equals("") ? declaredMethod.getName() : n;
                        if (parameterTypes.length > 1) {
                            throw new RuntimeException("Handler的参数长度不能超过1");
                        }
                        if (parameterTypes.length == 0) {
                            taskHandlers.add(new NoParam(n, declaredMethod, bean));
                        } else if (parameterTypes[0].equals(Task.class)) {
                            taskHandlers.add(new TaskType(n, declaredMethod, bean));
                        } else {
                            taskHandlers.add(new RealType(n, declaredMethod, bean));
                        }
                    }
                }
            }
        }

        MyTimerFactory factory = new MyTimerFactory();

        factory.setHandlers(taskHandlers);

        if (taskRepository != null) {
            factory.setTaskRepository(taskRepository);
        }

        return factory.create();

    }

    private static abstract class P implements TaskHandler {

        String name;

        Method method;

        Object bean;

        P(String name, Method method, Object bean) {
            this.name = name;
            method.setAccessible(true);
            this.method = method;
            this.bean = bean;
        }

        @Override
        public String name() {
            return name;
        }
    }

    private static class NoParam extends P {
        public NoParam(String name, Method method, Object bean) {
            super(name, method, bean);
        }

        @Override
        public void handle(Task task) {
            try {
                method.invoke(bean);
            } catch (IllegalAccessException | InvocationTargetException ignore) {
            }
        }
    }

    private static class RealType extends P {
        RealType(String name, Method method, Object bean) {
            super(name, method, bean);
        }

        @Override
        public void handle(com.gitee.beiding.timer.Task task) {
            Object payload = task.getPayload();
            try {
                method.invoke(bean, payload);
            } catch (IllegalAccessException | InvocationTargetException ignore) {
            }
        }
    }

    private static class TaskType extends P {

        TaskType(String name, Method method, Object bean) {
            super(name, method, bean);
        }

        @Override
        public void handle(Task task) {
            try {
                method.invoke(bean, task);
            } catch (IllegalAccessException | InvocationTargetException ignore) {
            }
        }
    }

}
